#include <WiFi.h>
#include <HTTPClient.h>
#include <driver/i2s.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_heap_caps.h>
#include <esp_psram.h>

// -------------------------- 配置参数 ---------------------------
// WiFi网络信息
const char* ssid = "ChinaNet-6x8c";        // WiFi网络名称
const char* password = "9zeymm9c";          // WiFi密码

// 服务器地址配置
const char* serverUrl = "http://192.168.1.12:8222/postWav";  // 音频上传服务器地址
const char* audioUrl = "http://192.168.1.12:8222/getVoice";  // 网络音频获取地址

// MS4030麦克风I2S引脚配置
#define I2S_PORT I2S_NUM_0                  // 使用I2S0接口
#define I2S_BCLK_PIN 46                     // 位时钟引脚（比特时钟）
#define I2S_LRCLK_PIN 17                    // 左右声道时钟引脚（字选择）
#define I2S_DATA_PIN 9                      // 数据输入引脚

// MAX98357音频放大器I2S引脚配置
#define I2S_SPEAKER_PORT I2S_NUM_1          // 使用I2S1接口
#define I2S_SPEAKER_BCLK_PIN 12             // 扬声器位时钟引脚
#define I2S_SPEAKER_LRCLK_PIN 13            // 扬声器左右声道时钟引脚
#define I2S_SPEAKER_DATA_PIN 14             // 扬声器数据输出引脚

// 音频参数配置
#define SAMPLE_RATE 16000                   // 采样率：16kHz（语音常用）
#define BITS_PER_SAMPLE 16                  // 位深度：16位
#define CHANNELS 1                          // 声道数：单声道
#define RECORD_TIME_MS 5000                 // 录音时长：5000毫秒（5秒）

// PSRAM缓冲区大小计算
// 计算公式：采样率 × 每样本字节数 × 声道数 × 录音时长(秒)
#define PSRAM_BUFFER_SIZE (SAMPLE_RATE * BITS_PER_SAMPLE / 8 * CHANNELS * RECORD_TIME_MS / 1000)
// -----------------------------------------------------------------

// 全局变量声明
uint8_t* audioBuffer = NULL;    // PSRAM音频缓冲区指针
size_t audioDataSize = 0;       // 实际音频数据大小
bool isRecording = false;       // 录音状态标志

// 初始化函数
void setup() {
  Serial.begin(115200);         // 初始化串口通信，波特率115200
  while (!Serial) delay(10);    // 等待串口就绪
  Serial.println("ESP32-S3 MS4030录音测试");

  // 初始化PSRAM（外部内存）
  if (psramInit()) {
    Serial.println("PSRAM初始化成功");
    // 分配PSRAM缓冲区，额外44字节用于WAV文件头
    audioBuffer = (uint8_t*)ps_malloc(PSRAM_BUFFER_SIZE + 44);
    if (audioBuffer) {
      Serial.printf("PSRAM缓冲区分配成功: %d 字节\n", PSRAM_BUFFER_SIZE + 44);
    } else {
      Serial.println("PSRAM缓冲区分配失败!");
      return;  // 分配失败则退出
    }
  } else {
    Serial.println("PSRAM初始化失败!");
    return;  // PSRAM初始化失败则退出
  }

  // 连接WiFi网络
  connectWiFi();

  // 初始化I2S麦克风
  if (initI2SMicrophone() != ESP_OK) {
    Serial.println("I2S麦克风初始化失败!");
    return;
  }
  Serial.println("I2S麦克风初始化成功");

  // 初始化MAX98357扬声器
  if (initMAX98357() != ESP_OK) {
    Serial.println("MAX98357初始化失败!");
    return;
  }
  Serial.println("MAX98357初始化成功");
  
  // 从网络获取并播放音频
  playNetworkWav(audioUrl);

  // 开始录音
  Serial.println("开始录音...");
  recordAudio();

  // 播放刚刚录制的音频（从PSRAM）
  Serial.println("播放PSRAM中的111.wav...");
  playAudioFromPSRAM();

  // 上传录音文件到服务器
  Serial.println("录音完成，开始上传...");
  uploadAudioToServer();

  Serial.println("程序执行完成");
}

// 主循环函数
void loop() {
  // 主循环为空，如需持续录音上传可在此添加逻辑
  delay(1000);
}

// WiFi连接函数
void connectWiFi() {
  Serial.printf("连接WiFi: %s ...", ssid);
  WiFi.begin(ssid, password);           // 启动WiFi连接
  while (WiFi.status() != WL_CONNECTED) { // 等待连接成功
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi连接成功！IP地址: " + WiFi.localIP().toString());
}

// I2S麦克风初始化函数
esp_err_t initI2SMicrophone() {
  // I2S配置结构体（用于录音）
  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),  // 主模式 + 接收模式
    .sample_rate = SAMPLE_RATE,                           // 采样率16kHz
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,         // 16位采样
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,          // 仅左声道（单声道）
    .communication_format = I2S_COMM_FORMAT_STAND_I2S,    // 标准I2S格式
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,             // 中断级别1
    .dma_buf_count = 8,                                   // DMA缓冲区数量
    .dma_buf_len = 512,                                   // 每个缓冲区长度
    .use_apll = false,                                    // 不使用音频锁相环
    .tx_desc_auto_clear = false,                          // 不自动清除发送描述符
    .fixed_mclk = 0                                       // 不固定主时钟
  };

  // 安装I2S驱动程序
  esp_err_t err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
  if (err != ESP_OK) {
    Serial.printf("I2S驱动安装失败: %d\n", err);
    return err;
  }

  // 配置I2S引脚
  i2s_pin_config_t pin_config = {
    .bck_io_num = I2S_BCLK_PIN,        // 位时钟引脚
    .ws_io_num = I2S_LRCLK_PIN,        // 字选择引脚
    .data_out_num = I2S_PIN_NO_CHANGE, // 数据输出引脚（不使用）
    .data_in_num = I2S_DATA_PIN        // 数据输入引脚
  };

  err = i2s_set_pin(I2S_PORT, &pin_config);
  if (err != ESP_OK) {
    Serial.printf("I2S引脚配置失败: %d\n", err);
    i2s_driver_uninstall(I2S_PORT);  // 失败时卸载驱动
    return err;
  }

  Serial.println("I2S麦克风配置完成");
  return ESP_OK;
}

// MAX98357扬声器初始化函数
esp_err_t initMAX98357() {
  // I2S配置结构体（用于播放）
  i2s_config_t i2sConfig = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),  // 主模式 + 发送模式
    .sample_rate = 16000,                                 // 采样率16kHz
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,         // 16位采样
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,          // 仅左声道输出
    .communication_format = I2S_COMM_FORMAT_STAND_I2S,    // 标准I2S格式
    .dma_buf_count = 8,                                   // DMA缓冲区数量
    .dma_buf_len = 512,                                   // 每个缓冲区长度
    .use_apll = false,                                    // 不使用音频锁相环
    .tx_desc_auto_clear = true,                           // 自动清除发送描述符
    .fixed_mclk = 0                                       // 不固定主时钟
  };

  // 安装I2S驱动程序
  esp_err_t err = i2s_driver_install(I2S_SPEAKER_PORT, &i2sConfig, 0, NULL);
  if (err != ESP_OK) {
    Serial.printf("I2S扬声器驱动安装失败: %d\n", err);
    return err;
  }

  // 配置I2S引脚
  i2s_pin_config_t pin_config = {
    .bck_io_num = I2S_SPEAKER_BCLK_PIN,    // 扬声器位时钟引脚
    .ws_io_num = I2S_SPEAKER_LRCLK_PIN,    // 扬声器字选择引脚
    .data_out_num = I2S_SPEAKER_DATA_PIN,  // 扬声器数据输出引脚
    .data_in_num = I2S_PIN_NO_CHANGE       // 数据输入引脚（不使用）
  };

  err = i2s_set_pin(I2S_SPEAKER_PORT, &pin_config);
  if (err != ESP_OK) {
    Serial.printf("I2S扬声器引脚配置失败: %d\n", err);
    i2s_driver_uninstall(I2S_SPEAKER_PORT);  // 失败时卸载驱动
    return err;
  }

  Serial.println("MAX98357扬声器配置完成");
  return ESP_OK;
}

// 录音函数（带软件增益优化）
void recordAudio() {
  if (!audioBuffer) {
    Serial.println("PSRAM缓冲区未分配!");
    return;
  }

  // 创建WAV文件头
  createWavHeader(audioBuffer, PSRAM_BUFFER_SIZE);

  size_t totalBytesRead = 0;          // 总读取字节数
  const size_t readSize = 1024;       // 每次读取1024字节（1KB）
  uint32_t startTime = millis();      // 录音开始时间
  
  // 软件增益设置（2-8倍，根据实际效果调整）
  const int softwareGain = 8;         // 软件增益倍数
  
  Serial.println("开始录音...");
  Serial.printf("软件增益设置: %d倍\n", softwareGain);

  // 录音指定时长
  while ((millis() - startTime) < RECORD_TIME_MS) {
    size_t bytesRead = 0;
    
    // 临时缓冲区用于原始数据读取
    int16_t tempBuffer[readSize / 2];  // 16位样本缓冲区（1024字节 = 512个样本）

    // 从I2S读取音频数据到临时缓冲区
    esp_err_t result = i2s_read(I2S_PORT,
                                tempBuffer,
                                readSize,
                                &bytesRead,
                                portMAX_DELAY);  // 无限等待

    if (result == ESP_OK && bytesRead > 0) {
      // 应用软件增益并复制到主缓冲区
      size_t samplesRead = bytesRead / 2;  // 16位样本数
      for (size_t i = 0; i < samplesRead; i++) {
        // 应用增益并限制在16位范围内避免削波
        int32_t amplifiedSample = (int32_t)tempBuffer[i] * softwareGain;
        
        // 限制在16位范围内（-32768 到 +32767）
        if (amplifiedSample > 32767) amplifiedSample = 32767;
        if (amplifiedSample < -32768) amplifiedSample = -32768;
        
        // 复制到主缓冲区（跳过44字节的WAV头）
        ((int16_t*)(audioBuffer + 44))[totalBytesRead / 2 + i] = (int16_t)amplifiedSample;
      }
      
      totalBytesRead += bytesRead;

      // 显示录音进度
      if (totalBytesRead % 4096 == 0) {
        int progress = ((millis() - startTime) * 100) / RECORD_TIME_MS;
        Serial.printf("录音进度: %d%%, 已录制: %d KB\n",
                      progress, totalBytesRead / 1024);
      }

      // 检查是否超出缓冲区
      if (totalBytesRead + 44 >= PSRAM_BUFFER_SIZE + 44) {
        Serial.println("缓冲区已满，停止录音");
        break;
      }
    } else {
      Serial.printf("I2S读取错误: %d\n", result);
      break;
    }
  }

  // 更新总数据大小（包含WAV头）
  audioDataSize = totalBytesRead + 44;
  Serial.printf("录音完成! 总数据量: %d 字节 (%.2f KB)\n",
                audioDataSize, audioDataSize / 1024.0);
  Serial.println("音频数据已保存到PSRAM，文件名为: 111.wav");
}

// 从PSRAM播放音频函数
void playAudioFromPSRAM() {
  if (!audioBuffer || audioDataSize == 0) {
    Serial.println("PSRAM中没有音频数据可播放");
    return;
  }

  Serial.println("开始播放PSRAM中的111.wav...");

  // 跳过WAV文件头（44字节），只播放音频数据
  uint8_t* audioData = audioBuffer + 44;
  size_t audioDataLength = audioDataSize - 44;

  size_t bytesWritten = 0;            // 单次写入字节数
  size_t totalBytesWritten = 0;       // 总写入字节数
  const size_t writeSize = 1024;      // 每次写入1KB

  uint32_t startTime = millis();      // 播放开始时间

  // 循环播放所有音频数据
  while (totalBytesWritten < audioDataLength) {
    size_t bytesToWrite = min(writeSize, audioDataLength - totalBytesWritten);

    // 写入数据到I2S扬声器
    esp_err_t result = i2s_write(I2S_SPEAKER_PORT,
                                 audioData + totalBytesWritten,
                                 bytesToWrite,
                                 &bytesWritten,
                                 portMAX_DELAY);  // 无限等待

    if (result == ESP_OK && bytesWritten > 0) {
      totalBytesWritten += bytesWritten;

      // 显示播放进度
      if (totalBytesWritten % 4096 == 0) {
        int progress = (totalBytesWritten * 100) / audioDataLength;
        Serial.printf("播放进度: %d%%, 已播放: %d KB\n",
                      progress, totalBytesWritten / 1024);
      }
    } else {
      Serial.printf("I2S写入错误: %d\n", result);
      break;
    }
  }

  uint32_t endTime = millis();
  Serial.printf("播放完成! 总播放时间: %d 毫秒\n", endTime - startTime);

  // 清空DMA缓冲区，确保播放完全结束
  i2s_zero_dma_buffer(I2S_SPEAKER_PORT);
}

// 创建WAV文件头函数
void createWavHeader(uint8_t* buffer, uint32_t dataSize) {
  // RIFF chunk描述符
  buffer[0] = 'R';  // RIFF标识
  buffer[1] = 'I';
  buffer[2] = 'F';
  buffer[3] = 'F';
  
  // 文件总大小 - 8字节（不包括RIFF标识和大小字段）
  uint32_t fileSize = dataSize + 36;
  memcpy(buffer + 4, &fileSize, 4);
  
  // WAVE格式标识
  buffer[8] = 'W';
  buffer[9] = 'A';
  buffer[10] = 'V';
  buffer[11] = 'E';

  // fmt子块
  buffer[12] = 'f';  // fmt标识
  buffer[13] = 'm';
  buffer[14] = 't';
  buffer[15] = ' ';
  
  uint32_t fmtSize = 16;  // PCM格式子块大小
  memcpy(buffer + 16, &fmtSize, 4);
  
  uint16_t audioFormat = 1;  // PCM格式 = 1
  memcpy(buffer + 20, &audioFormat, 2);
  
  uint16_t numChannels = CHANNELS;  // 声道数
  memcpy(buffer + 22, &numChannels, 2);
  
  uint32_t sampleRate = SAMPLE_RATE;  // 采样率
  memcpy(buffer + 24, &sampleRate, 4);
  
  // 字节率 = 采样率 × 声道数 × 位深度/8
  uint32_t byteRate = SAMPLE_RATE * CHANNELS * BITS_PER_SAMPLE / 8;
  memcpy(buffer + 28, &byteRate, 4);
  
  // 块对齐 = 声道数 × 位深度/8
  uint16_t blockAlign = CHANNELS * BITS_PER_SAMPLE / 8;
  memcpy(buffer + 32, &blockAlign, 2);
  
  uint16_t bitsPerSample = BITS_PER_SAMPLE;  // 位深度
  memcpy(buffer + 34, &bitsPerSample, 2);

  // data子块
  buffer[36] = 'd';  // data标识
  buffer[37] = 'a';
  buffer[38] = 't';
  buffer[39] = 'a';
  
  // 音频数据大小
  memcpy(buffer + 40, &dataSize, 4);
}

// 上传音频到服务器函数
void uploadAudioToServer() {
  if (!audioBuffer || audioDataSize == 0) {
    Serial.println("没有音频数据可上传");
    return;
  }

  HTTPClient http;  // HTTP客户端对象

  // 开始HTTP连接
  Serial.printf("连接到服务器: %s\n", serverUrl);
  if (!http.begin(serverUrl)) {
    Serial.println("HTTP初始化失败");
    return;
  }

  // 设置请求头
  http.addHeader("Content-Type", "audio/wav");           // 内容类型
  http.addHeader("Content-Length", String(audioDataSize)); // 内容长度

  // 发送POST请求，上传音频数据
  Serial.println("开始上传音频数据...");
  int httpResponseCode = http.POST(audioBuffer, audioDataSize);

  // 处理服务器响应
  if (httpResponseCode > 0) {
    Serial.printf("HTTP响应码: %d\n", httpResponseCode);
    String response = http.getString();
    Serial.printf("服务器响应: %s\n", response.c_str());
  } else {
    Serial.printf("HTTP请求失败，错误码: %d\n", httpResponseCode);
    Serial.printf("错误信息: %s\n", http.errorToString(httpResponseCode).c_str());
  }

  http.end();  // 关闭HTTP连接
}

// WAV文件格式验证函数（调试用）
void verifyWavFile() {
  if (!audioBuffer || audioDataSize < 44) {
    Serial.println("无效的音频数据");
    return;
  }

  Serial.println("验证WAV文件格式:");
  Serial.printf("文件大小: %d 字节\n", audioDataSize);

  // 检查RIFF头
  if (audioBuffer[0] == 'R' && audioBuffer[1] == 'I' && audioBuffer[2] == 'F' && audioBuffer[3] == 'F') {
    Serial.println("RIFF头: 正确");
  } else {
    Serial.println("RIFF头: 错误");
  }

  // 检查WAVE格式标识
  if (audioBuffer[8] == 'W' && audioBuffer[9] == 'A' && audioBuffer[10] == 'V' && audioBuffer[11] == 'E') {
    Serial.println("WAVE格式: 正确");
  } else {
    Serial.println("WAVE格式: 错误");
  }

  // 提取并显示音频参数
  uint16_t channels = *(uint16_t*)(audioBuffer + 22);        // 声道数
  uint32_t sampleRate = *(uint32_t*)(audioBuffer + 24);      // 采样率
  uint16_t bitsPerSample = *(uint16_t*)(audioBuffer + 34);   // 位深度
  uint32_t dataSize = *(uint32_t*)(audioBuffer + 40);        // 音频数据大小

  Serial.printf("声道数: %d\n", channels);
  Serial.printf("采样率: %d Hz\n", sampleRate);
  Serial.printf("位深度: %d bit\n", bitsPerSample);
  Serial.printf("音频数据大小: %d 字节\n", dataSize);
}

// 释放PSRAM内存函数
void freeAudioBuffer() {
  if (audioBuffer) {
    free(audioBuffer);
    audioBuffer = NULL;
    audioDataSize = 0;
    Serial.println("PSRAM音频缓冲区已释放");
  }
}

// 从网络获取WAV文件并通过I2S播放函数
void playNetworkWav(const char* url) {
  HTTPClient http;

  // 启动HTTP连接
  if (!http.begin(url)) {
    Serial.println("HTTP初始化失败");
    return;
  }

  // 发送HTTP GET请求
  int httpCode = http.GET();
  if (httpCode != HTTP_CODE_OK) {
    Serial.printf("HTTP请求失败，错误码: %d\n", httpCode);
    http.end();
    return;
  }

  // 获取音频数据流
  WiFiClient* stream = http.getStreamPtr();
  if (!stream) {
    Serial.println("无法获取音频流");
    http.end();
    return;
  }

  // 跳过WAV文件头部（标准PCM WAV头部为44字节）
  uint8_t wavHeader[44];
  size_t headerRead = stream->readBytes(wavHeader, 44);
  if (headerRead != 44) {
    Serial.println("WAV文件头部不完整，可能不是标准PCM文件");
    http.end();
    return;
  }
  Serial.println("已跳过WAV头部，开始播放音频数据");

  // 读取音频数据并通过I2S输出
  const size_t bufferSize = 1024;  // 数据缓冲区大小
  uint8_t audioBuffer[bufferSize];
  size_t totalBytesPlayed = 0;

  // 循环读取并播放网络音频数据
  while (stream->available()) {
    // 从网络读取数据到缓冲区
    size_t bytesRead = stream->readBytes(audioBuffer, bufferSize);
    if (bytesRead == 0) break;

    // 通过I2S输出音频数据
    size_t bytesWritten = 0;
    esp_err_t err = i2s_write(I2S_SPEAKER_PORT, audioBuffer, bytesRead, &bytesWritten, portMAX_DELAY);
    if (err != ESP_OK) {
      Serial.printf("I2S写入失败，错误码: %d\n", err);
      break;
    }

    totalBytesPlayed += bytesWritten;
    // 每播放4KB数据打印一次进度
    if (totalBytesPlayed % 4096 == 0) {
      Serial.printf("已播放: %.2f KB\n", totalBytesPlayed / 1024.0);
    }
  }

  Serial.printf("播放完成，总数据量: %.2f KB\n", totalBytesPlayed / 1024.0);
  http.end();  // 关闭HTTP连接
}